/* 
 * File:   BVHNode.cpp
 * Author: brady
 * 
 * Created on August 27, 2011, 5:14 PM
 */

#include <acceleration/BVHNode.h>

BVHNode::BVHNode() {
    leftChild = NULL;
    rightChild = NULL;
    shape = NULL;
}

BVHNode::BVHNode(vector<Shape*>& shapes, int sortAxis){
    this->leftChild = NULL;
    this->rightChild = NULL;
    this->shape = NULL;
    if(shapes.size() == 0){
        this->boundingBox = BoundingBox::inverseBox();
        return;
    }
    else if(shapes.size() == 1)
    {
        this->shape = shapes[0];
        this->shader = shape->getShader();
        this->boundingBox = shape->getBoundingBox();
    }
    else {
        ShapeComparitor sc = ShapeComparitor(sortAxis);
        sort(shapes.begin(), shapes.end(), sc);
        vector<Shape*> leftNodes;
        vector<Shape*> rightNodes;
        int pivotIndex = ((shapes.size()-1) / 2);
        for (int i = 0; i < shapes.size(); i++) {
            if (i <= pivotIndex) {
                leftNodes.push_back(shapes[i]);
            } else {
                rightNodes.push_back(shapes[i]);
            }
        }
        leftChild = new BVHNode(leftNodes, (sortAxis+1) % 3);
        rightChild = (!rightNodes.empty()) ?
            new BVHNode(rightNodes, (sortAxis+1) % 3) :
            NULL;
        boundingBox = leftChild->getBoundingBox();
        if(rightChild != NULL){
            boundingBox = boundingBox.encompass(rightChild->getBoundingBox());
        }
    }
}

BVHNode::BVHNode(const BVHNode& orig) {
    this->boundingBox = orig.boundingBox;
    this->leftChild = orig.leftChild;
    this->rightChild = orig.rightChild;
    this->shape = orig.shape;
}

BVHNode::~BVHNode() {
    if(leftChild != NULL)
    leftChild->~BVHNode();
    if(rightChild != NULL)
    rightChild->~BVHNode();
}

bool BVHNode::testIntersect(IntersectionParams& ip, IntersectionResult& ir){

    //check to see if we miss the bounding box
    bool hit = boundingBox.testIntersect(ip, NULL, NULL);
    
    if(!hit){
        return false;
    }

    //we hit this box
    if(shape != NULL){
        return shape->testIntersect(ip, ir);
    }
    //we at least have a left child
    IntersectionResult left_ir;
    left_ir.tVal = DBL_MAX;
    IntersectionResult right_ir;
    right_ir.tVal = DBL_MAX;
    bool leftHit = false;
    bool rightHit = false;
    leftHit = leftChild->testIntersect(ip, left_ir);
    if(rightChild != NULL){
        rightHit = rightChild->testIntersect(ip, right_ir);
    }
    if(left_ir.tVal < right_ir.tVal){
        ir.set(left_ir);
        return leftHit;
    }
    else{
        ir.set(right_ir);
        return rightHit;
    }
}

void BVHNode::boundingBoxIntersections(const BoundingBox& box, vector<Shape*>& list){
    if(box.overlaps(this->boundingBox)){
        if(isLeaf()){
            list.push_back(shape);
        }
        else{
            if(leftChild != NULL) leftChild->boundingBoxIntersections(box, list);
            if(rightChild != NULL) rightChild->boundingBoxIntersections(box, list);
        }
    }
}